package com.quxiu.system.service.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.quxiu.common.core.exception.ServiceException;
import com.quxiu.common.core.utils.DateUtils;
import com.quxiu.common.core.utils.bean.BeanUtils;
import com.quxiu.system.api.domain.SysDept;
import com.quxiu.system.api.domain.SysUser;
import com.quxiu.system.constant.*;
import com.quxiu.system.domain.*;
import com.quxiu.system.domain.vo.*;
import com.quxiu.system.mapper.*;
import com.quxiu.system.service.IBpmApprovalService;
import com.quxiu.system.service.IdService;
import com.quxiu.system.utils.BpmConditionUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import com.quxiu.system.service.IBpmProcInstService;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;

/**
 * 流程实例Service业务层处理
 *
 * @author quxiu
 * @date 2024-06-14
 */
@Service
public class BpmProcInstServiceImpl implements IBpmProcInstService {
    @Resource
    private BpmProcInstMapper bpmProcInstMapper;
    @Resource
    private BpmProcMapper bpmProcMapper;
    @Resource
    private BpmProcVersionMapper bpmProcVersionMapper;
    @Resource
    private BpmProcNodeMapper bpmProcNodeMapper;
    @Resource
    private SysUserMapper sysUserMapper;
    @Resource
    private SysRoleMapper sysRoleMapper;
    @Resource
    private SysDeptMapper sysDeptMapper;
    @Resource
    private IdService idService;
    @Resource
    private BpmFormDataMapper bpmFormDataMapper;
    @Resource
    private BpmProcInstNodeMapper bpmProcInstNodeMapper;
    @Resource
    private BpmApprovalRecordMapper bpmApprovalRecordMapper;
    @Resource
    private IBpmApprovalService bpmApprovalService;
    @Resource
    private BpmProcActorMapper bpmProcActorMapper;

    /**
     * 查询流程实例
     *
     * @param procInstId 流程实例主键
     * @return 流程实例
     */
    @Override
    public BpmProcInstInfoVo getBpmProcInstInfo(Long procInstId, Long userId) {
        BpmProcInst bpmProcInst = bpmProcInstMapper.selectInstByProcInstId(procInstId);
        if (bpmProcInst == null) {
            throw new ServiceException("流程实例不存在");
        }
        BpmProcInstInfoVo vo = new BpmProcInstInfoVo();
        BeanUtils.copyBeanProp(vo, bpmProcInst);

        // 获取表单配置
        BpmProcVersion version = bpmProcVersionMapper.selectByVersionId(vo.getVersionId());
        if (version != null) {
            vo.setFormConfig(JSONObject.parseObject(version.getFormConfig()));
            vo.setAllowAddSign(version.getAllowAddSign());
            vo.setAllowAutoApprove(version.getAllowAutoApprove());
            vo.setAllowForward(version.getAllowForward());
            vo.setAllowOpinionEmpty(version.getAllowOpinionEmpty());
            vo.setAllowRevoke(version.getAllowRevoke());
            vo.setAllowRollback(version.getAllowRollback());
        }

        // 获取表单数据
        BpmFormData formData = bpmFormDataMapper.selectByProcInstId(procInstId);
        if (formData != null) {
            vo.setFormData(JSONObject.parseObject(formData.getData()));
        }

        // 当前登录人是否需要审批
        vo.setNeedApproval(false);
        BpmProcActor actor = bpmProcActorMapper.select(procInstId, BpmProcActorType.APPROVER, userId);
        if (actor != null) {
            if (BpmProcActorHandleStatus.WAIT.equals(actor.getHandleStatus())) {
                vo.setNeedApproval(true);
            }

            vo.setHandleRecordId(actor.getHandleRecordId());
            BpmApprovalRecord record = bpmApprovalRecordMapper.selectByRecordId(actor.getHandleRecordId());
            if (record != null) {
                vo.setHandleNodeType(record.getNodeType());
                vo.setHandlerId(record.getHandlerId());
                vo.setHandlerName(record.getHandlerName());
                BpmProcInstNode node = bpmProcInstNodeMapper.selectByNodeId(record.getProcInstId(), record.getNodeId());
                BpmNodeConfig config = JSONObject.parseObject(node.getConfig(), BpmNodeConfig.class);
                // 设置数据权限
                vo.setPermissions(config.getPermissions());

                if (BpmNodeType.APPROVER.equals(node.getNodeType()) && BpmHandleModel.SEQUENCE_SIGN != config.getHandleModel()) {
                    // 会签/或签审批不能进行加签操作
                    vo.setAllowAddSign(0);
                }
            }
        }

        // 如果没有权限，则尝试获取抄送权限
        if (vo.getPermissions() == null) {
            BpmProcActor notifyActor = bpmProcActorMapper.select(procInstId, BpmProcActorType.NOTIFY, userId);
            if (notifyActor != null) {
                BpmApprovalRecord record = bpmApprovalRecordMapper.selectByRecordId(notifyActor.getHandleRecordId());
                if (record != null) {
                    BpmProcInstNode node = bpmProcInstNodeMapper.selectByNodeId(record.getProcInstId(), record.getNodeId());
                    BpmNodeConfig config = JSONObject.parseObject(node.getConfig(), BpmNodeConfig.class);
                    // 设置数据权限
                    vo.setPermissions(config.getPermissions());
                }
            }
        }

        // 判断当前用户是否为审批管理员
        vo.setAdmin(false);
        // 判断当前用户是否为申请人
        if (userId.equals(vo.getOwnerId())) {
            vo.setOwner(true);
        }
        return vo;
    }

    /**
     * 查询我的流程实例列表
     *
     * @param param 流程实例
     * @return 流程实例
     */
    @Override
    public List<BpmProcInst> myApproveList(BpmApproveSearchParam param) {
        if (param.getSearchType() == null) {
            param.setSearchType(1);
        }
        switch (param.getSearchType()) {
            case 1:
                param.setActorType(BpmProcActorType.APPLICAT);
                param.setActorStatus(BpmProcActorHandleStatus.HANDLED);
                break;
            case 2:
                param.setActorType(BpmProcActorType.APPROVER);
                param.setActorStatus(BpmProcActorHandleStatus.WAIT);
                break;
            case 3:
                param.setActorType(BpmProcActorType.APPROVER);
                param.setActorStatus(BpmProcActorHandleStatus.HANDLED);
                break;
            case 4:
                param.setActorType(BpmProcActorType.NOTIFY);
                if (param.getOnlyUnread() != null && param.getOnlyUnread()) {
                    param.setActorStatus(BpmProcActorHandleStatus.WAIT);
                }
                break;
            default:
                param.setActorType(BpmProcActorType.APPLICAT);
                param.setActorStatus(BpmProcActorHandleStatus.HANDLED);
        }
        return bpmProcInstMapper.myApproveList(param);
    }

    /**
     * 查询流程实例列表
     *
     * @param param 流程实例
     * @return 流程实例
     */
    @Override
    public List<BpmProcInst> handoverList(BpmApproveSearchParam param) {

        return bpmProcInstMapper.handoverList(param);
    }

    /**
     * 新增流程实例
     *
     * @param param 流程实例
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void insertBpmProcInst(BpmProcInstVo param) {
        BpmProc proc = bpmProcMapper.selectBpmProcByProcId(param.getProcId());
        if (proc == null) {
            throw new ServiceException("无效的流程id");
        }
        BpmProcVersion version = bpmProcVersionMapper.selectByVersionId(param.getVersionId());
        if (version == null) {
            throw new ServiceException("无效的流程版本id");
        }
        SysUser user = sysUserMapper.selectUserById(param.getOwnerId());
        if (user == null) {
            throw new ServiceException("无效的用户id");
        }
        SysDept dept = sysDeptMapper.selectDeptById(user.getDeptId());
        BpmProcInst inst = new BpmProcInst();
        inst.setProcInstId(idService.nextId());
        inst.setProcId(proc.getProcId());
        String procName = String.format("%s提交的%s", user.getNickName(), proc.getProcName());
        inst.setProcName(procName);
        inst.setVersionId(param.getVersionId());
        inst.setOwnerId(param.getOwnerId());
        inst.setOwnerName(user.getNickName());
        inst.setRunStatus(BpmInstRunStatus.NOT_STARTED);
        inst.setApprovalStatus(BpmInstApprovalStatus.DRAFT);
        if (dept != null) {
            inst.setDeptId(dept.getDeptId());
            inst.setDeptName(dept.getDeptName());
        }
        inst.setCreateTime(DateUtils.getNowDate());
        bpmProcInstMapper.insertBpmProcInst(inst);

        // 保存表单数据
        this.saveBpmFormData(user.getUserId(), inst.getProcInstId(), param.getFormData());

        // 创建流程实例节点
        this.createBpmProcInstNode(version, inst, param.getNodes());

        if (param.getSubmit()) {
            bpmApprovalService.submitProcInst(inst.getProcInstId());
        }
    }


    /**
     * 创建流程实例节点
     */
    private void createBpmProcInstNode(BpmProcVersion version, BpmProcInst inst, List<BpmNodeByApplyVo> nodes) {

        List<BpmProcNode> bpmProcNodeList = bpmProcNodeMapper.selectBpmProcNodeListByVersionId(inst.getVersionId());
        Map<Long, BpmProcNode> bpmProcNodeMap = bpmProcNodeList.stream().collect(Collectors.toMap(BpmProcNode::getNodeId, Function.identity()));
        List<BpmProcInstNode> instNodeList = new ArrayList<>();
        List<BpmApprovalRecord> records = new ArrayList<>();
        BpmProcInstNode parentInstNode = null;
        for (BpmNodeByApplyVo node: nodes) {
            if (CollectionUtils.isEmpty(node.getHandlerList())) {
                continue;
            }
            long parentId = parentInstNode == null ? 0 : parentInstNode.getNodeId();
            BpmProcNode nodeInfo = bpmProcNodeMap.get(node.getNodeId());
            BpmNodeConfig nodeConfig = JSONObject.parseObject(nodeInfo.getConfig(), BpmNodeConfig.class);
            BpmProcInstNode instNode = new BpmProcInstNode();
            instNode.setNodeId(idService.nextId());
            instNode.setParentId(parentId);
            instNode.setProcInstId(inst.getProcInstId());
            instNode.setNodeType(node.getNodeType());
            instNode.setNodeName(node.getNodeName());
            instNode.setStatus(BpmInstNodeStatus.NOT_STARTED);
            instNode.setConfig(nodeInfo.getConfig());
            instNode.setHandlers(JSONArray.toJSONString(node.getHandlerList()));
            instNode.setOriginNodeId(instNode.getNodeId());
            parentInstNode = instNode;
            instNodeList.add(instNode);

            long recordParentId = 0L;
            for (BpmHandler handler : node.getHandlerList()) {
                BpmApprovalRecord record = new BpmApprovalRecord();
                record.setRecordId(idService.nextId());
                record.setProcInstId(inst.getProcInstId());
                record.setParentId(recordParentId);
                record.setNodeId(instNode.getNodeId());
                record.setNodeType(instNode.getNodeType());
                record.setNodeName(instNode.getNodeName());
                record.setHandlerId(handler.getUserId());
                record.setHandlerName(handler.getName());
                record.setHandleStatus(BpmApprovalRecordHandleStatus.NOT_STARTED);
                if (BpmNodeType.APPROVER.equals(node.getNodeType()) && version.getAllowAutoApprove() != null) {
                    record.setJoinAutoApprove(version.getAllowAutoApprove());
                } else {
                    record.setJoinAutoApprove(0);
                }

                record.setCreateTime(DateUtils.getNowDate());
                records.add(record);

                if (BpmNodeType.APPROVER.equals(node.getNodeType())
                        && BpmHandleModel.SEQUENCE_SIGN == nodeConfig.getHandleModel()) {
                    recordParentId = record.getRecordId();
                }
            }
        }

        bpmProcInstMapper.updateNodeConfig(inst.getProcInstId(), JSONArray.toJSONString(instNodeList));
        bpmProcInstNodeMapper.insertBatch(instNodeList);
        bpmApprovalRecordMapper.insertBatch(records);
    }

    /**
     * 保存表单数据
     * @param userId 用户id
     * @param procInstId 流程实例id
     * @param data 表单数据
     */
    private void saveBpmFormData(Long userId, Long procInstId, JSONObject data) {
        bpmFormDataMapper.deleteByProcInstId(procInstId);

        BpmFormData formData = new BpmFormData();
        formData.setFormDataId(idService.nextId());
        formData.setProcInstId(procInstId);
        formData.setData(data.toJSONString());
        formData.setCreateBy(userId);
        formData.setCreateTime(DateUtils.getNowDate());
        bpmFormDataMapper.insert(formData);
    }

    /**
     * 修改流程实例
     *
     * @param bpmProcInst 流程实例
     * @return 结果
     */
    @Override
    public int updateBpmProcInst(BpmProcInst bpmProcInst) {
        return bpmProcInstMapper.updateBpmProcInst(bpmProcInst);
    }

    /**
     * 批量删除流程实例
     *
     * @param procInstIds 需要删除的流程实例主键
     * @return 结果
     */
    @Override
    public int deleteBpmProcInstByProcInstIds(Long[] procInstIds) {
        return bpmProcInstMapper.deleteBpmProcInstByProcInstIds(procInstIds);
    }

    /**
     * 删除流程实例信息
     *
     * @param procInstId 流程实例主键
     * @return 结果
     */
    @Override
    public int deleteBpmProcInstByProcInstId(Long procInstId) {
        return bpmProcInstMapper.deleteBpmProcInstByProcInstId(procInstId);
    }

    @Override
    public List<BpmNodeByApplyVo> calculateNodeInst(BpmProcInstVo param) {
        SysUser user = sysUserMapper.selectUserById(param.getOwnerId());
        param.setOwnerName(user.getNickName());
        List<BpmProcNode> bpmProcNodeList = bpmProcNodeMapper.selectBpmProcNodeListByVersionId(param.getVersionId());
        List<BpmNodeByApplyVo> result = new ArrayList<>();
        BpmProcNode firstNode = getNodeByParentId(0L, bpmProcNodeList);
        if (firstNode != null) {
            this.calculateNodeInst(bpmProcNodeList, firstNode, param, result);
        }
        return result;
    }

    @Override
    public List<BpmApprovalRecordVo> getApprovalRecord(Long procInstId) {
        List<BpmApprovalRecordVo> result = new ArrayList<>();
        List<BpmProcInstNode> nodes = bpmProcInstNodeMapper.selectByProcInstId(procInstId);
        // 排序
        nodes = sortNode(nodes);
        if (CollectionUtils.isNotEmpty(nodes)) {
            for (BpmProcInstNode node : nodes) {
                BpmNodeConfig config = JSONObject.parseObject(node.getConfig(), BpmNodeConfig.class);
                List<BpmApprovalRecord> records = bpmApprovalRecordMapper.selectByNodeId(procInstId, node.getNodeId());
                if (CollectionUtils.isNotEmpty(records)) {
                    if (BpmInstNodeStatus.NOT_STARTED.equals(node.getStatus())) {
                        BpmApprovalRecordVo vo = new BpmApprovalRecordVo();
                        vo.setNodeId(node.getNodeId());
                        vo.setNodeName(node.getNodeName());
                        vo.setNodeType(node.getNodeType());
                        vo.setStatus(node.getStatus());
                        vo.setHandleModel(config.getHandleModel());
                        vo.setHandlerScope(config.getHandlerScope());
                        for (BpmApprovalRecord record : records) {
                            BpmHandler handler = new BpmHandler();
                            handler.setUserId(record.getHandlerId());
                            handler.setName(record.getHandlerName());
                            vo.addHandler(handler);
                        }
                        if (BpmNodeType.APPROVER.equals(node.getNodeType())) {
                            if (vo.getHandlers().size() == 1) {
                                vo.setHandlerId(vo.getHandlers().get(0).getUserId());
                                vo.setHandlerName(vo.getHandlers().get(0).getName());
                                vo.setHandlers(null);
                            }
                        }

                        result.add(vo);
                    } else if (BpmInstNodeStatus.COMPLETED.equals(node.getStatus())) {
                        if (BpmNodeType.NOTIFY.equals(node.getNodeType())) {
                            BpmApprovalRecordVo vo = new BpmApprovalRecordVo();
                            vo.setNodeId(node.getNodeId());
                            vo.setNodeName(node.getNodeName());
                            vo.setNodeType(node.getNodeType());
                            vo.setStatus(node.getStatus());
                            vo.setHandleModel(config.getHandleModel());
                            vo.setHandlerScope(config.getHandlerScope());
                            for (BpmApprovalRecord record : records) {
                                BpmHandler handler = new BpmHandler();
                                handler.setUserId(record.getHandlerId());
                                handler.setName(record.getHandlerName());
                                vo.addHandler(handler);
                            }
                            result.add(vo);
                        } else {
                            for (BpmApprovalRecord record : records) {
                                BpmApprovalRecordVo vo = new BpmApprovalRecordVo();
                                vo.setNodeId(node.getNodeId());
                                vo.setNodeName(node.getNodeName());
                                vo.setNodeType(node.getNodeType());
                                vo.setStatus(node.getStatus());
                                vo.setHandleModel(config.getHandleModel());
                                vo.setHandlerScope(config.getHandlerScope());
                                vo.setHandlerId(record.getHandlerId());
                                vo.setHandlerName(record.getHandlerName());
                                vo.setHandleStatus(record.getHandleStatus());
                                vo.setHandleResult(record.getHandleResult());
                                vo.setHandleOpinion(record.getHandleOpinion());
                                vo.setHandleTime(record.getHandleTime());
                                result.add(vo);
                            }
                        }

                    } else if (BpmInstNodeStatus.RUNNING.equals(node.getStatus())) {
                        // 处理已审批记录
                        for (BpmApprovalRecord record : records) {
                            if (BpmApprovalRecordHandleStatus.COMPLETED.equals(record.getHandleStatus())) {
                                BpmApprovalRecordVo vo = new BpmApprovalRecordVo();
                                vo.setNodeId(node.getNodeId());
                                vo.setNodeName(node.getNodeName());
                                vo.setNodeType(node.getNodeType());
                                vo.setStatus(BpmInstNodeStatus.COMPLETED);
                                vo.setHandleModel(config.getHandleModel());
                                vo.setHandlerScope(config.getHandlerScope());
                                vo.setHandlerId(record.getHandlerId());
                                vo.setHandlerName(record.getHandlerName());
                                vo.setHandleStatus(record.getHandleStatus());
                                vo.setHandleResult(record.getHandleResult());
                                vo.setHandleOpinion(record.getHandleOpinion());
                                vo.setHandleTime(record.getHandleTime());
                                result.add(vo);
                            }
                        }

                        // 处理审批中及未审批记录
                        BpmApprovalRecordVo vo = new BpmApprovalRecordVo();
                        vo.setNodeId(node.getNodeId());
                        vo.setNodeName(node.getNodeName());
                        vo.setNodeType(node.getNodeType());
                        vo.setStatus(node.getStatus());
                        vo.setHandleModel(config.getHandleModel());
                        vo.setHandlerScope(config.getHandlerScope());
                        for (BpmApprovalRecord record : records) {
                            if (!BpmApprovalRecordHandleStatus.COMPLETED.equals(record.getHandleStatus())) {
                                BpmHandler handler = new BpmHandler();
                                handler.setUserId(record.getHandlerId());
                                handler.setName(record.getHandlerName());
                                vo.addHandler(handler);
                            }
                        }
                        if (vo.getHandlers().size() == 1) {
                            vo.setHandlerId(vo.getHandlers().get(0).getUserId());
                            vo.setHandlerName(vo.getHandlers().get(0).getName());
                            vo.setHandlers(null);
                        }
                        result.add(vo);
                    }
                }
            }
        }
        return result;
    }

    private List<BpmProcInstNode> sortNode(List<BpmProcInstNode> nodes) {
        if (CollectionUtils.isEmpty(nodes)) {
            return null;
        }
        List<BpmProcInstNode> result = new ArrayList<>();
        Map<Long, BpmProcInstNode> map = nodes.stream().collect(Collectors.toMap(BpmProcInstNode::getParentId, Function.identity()));
        Long parentId = 0L;
        while (true) {
            BpmProcInstNode node = map.get(parentId);
            if (node == null) {
                break;
            }
            result.add(node);
            parentId = node.getNodeId();
        }
        return result;
    }

    private BpmProcNode getNodeByParentId(Long parentId, List<BpmProcNode> bpmProcNodeList) {
        for (BpmProcNode node : bpmProcNodeList) {
            if (node.getParentId().equals(parentId)) {
                return node;
            }
        }
        return null;
    }

    private BpmProcNode getNodeByNodeId(Long nodeId, List<BpmProcNode> bpmProcNodeList) {
        for (BpmProcNode node : bpmProcNodeList) {
            if (node.getNodeId().equals(nodeId)) {
                return node;
            }
        }
        return null;
    }

    private void calculateNodeInst(List<BpmProcNode> nodes, BpmProcNode node, BpmProcInstVo param, List<BpmNodeByApplyVo> result) {
        BpmProcNode nextNode = null;
        switch (node.getNodeType()) {
            case BpmNodeType.START:
                nextNode = this.createNodeForStart(nodes, node, param, result);
                break;
            case BpmNodeType.APPROVER:
                nextNode = this.createNodeForApprover(nodes, node, param, result);
                break;
            case BpmNodeType.ROUTER:
                nextNode = this.createNodeForRouter(nodes, node, param, result);
                break;
            case BpmNodeType.NOTIFY:
                nextNode = this.createNodeForNotify(nodes, node, param, result);
                break;
        }

        if (nextNode != null) {
            this.calculateNodeInst(nodes, nextNode, param, result);
        }
    }

    private BpmProcNode createNodeForStart(List<BpmProcNode> nodes, BpmProcNode node, BpmProcInstVo param, List<BpmNodeByApplyVo> result) {
        BpmNodeByApplyVo vo = new BpmNodeByApplyVo();
        vo.setNodeId(node.getNodeId());
        vo.setNodeName(node.getNodeName());
        vo.setNodeType(node.getNodeType());
        vo.addHandler(param.getOwnerId(), param.getOwnerName());

        result.add(vo);

        return getNextNode(nodes, node);
    }

    private BpmProcNode createNodeForApprover(List<BpmProcNode> nodes, BpmProcNode node, BpmProcInstVo param, List<BpmNodeByApplyVo> result) {
        BpmNodeConfig config = JSONObject.parseObject(node.getConfig(), BpmNodeConfig.class);
        BpmNodeByApplyVo vo = new BpmNodeByApplyVo();
        vo.setNodeId(node.getNodeId());
        vo.setNodeName(node.getNodeName());
        vo.setNodeType(node.getNodeType());
        vo.setHandlerScope(config.getHandlerScope());
        vo.setHandleModel(config.getHandleModel());

        if (config.getHandlerScope() == BpmHandlerScope.OPTIONAL) {
            vo.setSelfSelectionType(config.getSelfSelectionType());
            vo.setSelfSelectionScope(config.getSelfSelectionScope());
            vo.setSelfSelectionItems(config.getSelfSelectionItems());
        } else {
            if (config.getHandlerScope() == BpmHandlerScope.APPOINT) {
                config.getHandlers().forEach(item -> {
                    if (UserScopeType.USER.equals(item.getType())) {
                        vo.addHandler(item.getId(), item.getLabel());
                    } else if (UserScopeType.ROLE.equals(item.getType())) {
                        List<SysUser> userList = sysRoleMapper.getUserListByRoleId(item.getId());
                        if (CollectionUtils.isNotEmpty(userList)) {
                            userList.forEach(user -> {
                                vo.addHandler(user.getUserId(), user.getNickName());
                            });
                        }
                    }
                });
            } else if (config.getHandlerScope() == BpmHandlerScope.DEPT_PRINCIPAL) {
                SysUser user = sysUserMapper.selectUserById(param.getOwnerId());
                if (user != null) {
                    SysDept dept = sysDeptMapper.selectDeptById(user.getDeptId());
                    if (dept != null) {
                        if (dept.getLeader() != null) {
                            SysUser leader = sysUserMapper.selectUserById(dept.getLeader());
                            if (leader != null) {
                                vo.addHandler(leader.getUserId(), leader.getNickName());
                            }
                        }
                    }
                }

            } else if (config.getHandlerScope() == BpmHandlerScope.DEPT_PRINCIPAL_MULTISTEP) {
                SysUser user = sysUserMapper.selectUserById(param.getOwnerId());
                if (user != null) {
                    Long deptId = user.getDeptId();
                    for (int i=0; i<config.getLeaderHierarchyLimit(); i++) {
                        if (deptId != null) {
                            SysDept dept = sysDeptMapper.selectDeptById(deptId);
                            if (dept != null) {
                                if (dept.getLeader() != null) {
                                    SysUser leader = sysUserMapper.selectUserById(dept.getLeader());
                                    if (leader != null) {
                                        vo.addHandler(leader.getUserId(), leader.getNickName());
                                    }
                                }
                                deptId = dept.getParentId();
                            } else {
                                break;
                            }
                        } else {
                            break;
                        }
                    }
                }
            }
        }
        result.add(vo);
        return getNextNode(nodes, node);
    }

    private BpmProcNode createNodeForNotify(List<BpmProcNode> nodes, BpmProcNode node, BpmProcInstVo param, List<BpmNodeByApplyVo> result) {
        BpmNodeConfig config = JSONObject.parseObject(node.getConfig(), BpmNodeConfig.class);
        BpmNodeByApplyVo vo = new BpmNodeByApplyVo();
        vo.setNodeId(node.getNodeId());
        vo.setNodeName(node.getNodeName());
        vo.setNodeType(node.getNodeType());

        if (config.getHandlerScope() == BpmHandlerScope.APPOINT) {
            config.getHandlers().forEach(item -> {
                if (UserScopeType.USER.equals(item.getType())) {
                    vo.addHandler(item.getId(), item.getLabel());
                } else if (UserScopeType.ROLE.equals(item.getType())) {
                    List<SysUser> userList = sysRoleMapper.getUserListByRoleId(item.getId());
                    if (CollectionUtils.isNotEmpty(userList)) {
                        userList.forEach(user -> {
                            vo.addHandler(user.getUserId(), user.getNickName());
                        });
                    }
                }
            });
        } else if (config.getHandlerScope() == BpmHandlerScope.DEPT_PRINCIPAL) {
            SysUser user = sysUserMapper.selectUserById(param.getOwnerId());
            if (user != null) {
                SysDept dept = sysDeptMapper.selectDeptById(user.getDeptId());
                if (dept != null) {
                    if (dept.getLeader() != null) {
                        SysUser leader = sysUserMapper.selectUserById(dept.getLeader());
                        if (leader != null) {
                            vo.addHandler(leader.getUserId(), leader.getNickName());
                        }
                    }
                }
            }

        }

        result.add(vo);

        return getNextNode(nodes, node);
    }
    private BpmProcNode createNodeForRouter(List<BpmProcNode> nodes, BpmProcNode node, BpmProcInstVo param, List<BpmNodeByApplyVo> result) {
        BpmNodeConfig config = JSONObject.parseObject(node.getConfig(), BpmNodeConfig.class);
        List<BpmProcNode> conditionNodes = new ArrayList<>();
        for (BpmProcNode item: nodes) {
            if (node.getNodeId().equals(item.getParentId())) {
                if (item.getNodeType().equals(BpmNodeType.CONDITION)) {
                    conditionNodes.add(item);
                }
            }
        }
        if (CollectionUtils.isNotEmpty(conditionNodes)) {
            for (BpmProcNode conditionNode: conditionNodes) {
                BpmNodeConfig conditionNodeConfig = JSONObject.parseObject(conditionNode.getConfig(), BpmNodeConfig.class);
                boolean isTure = BpmConditionUtil.calculate(conditionNodeConfig.getConditions(), param.getFormData());
                if (isTure) {
                    return getNextNode(nodes, conditionNode);
                }
            }
        }
        throw new RuntimeException("没有符合条件的路由");
    }

    private BpmProcNode getNextNode(List<BpmProcNode> nodes, BpmProcNode node) {
        BpmProcNode nextNode = null;
        if (node.getSkipNodeId() != null) {
            nextNode = getNodeByNodeId(node.getSkipNodeId(), nodes);
        } else {
            nextNode = getNodeByParentId(node.getNodeId(), nodes);
        }
        return nextNode;
    }


    @Override
    public BpmApprovalBoard getApprovalBoard(Long userId) {
        BpmApprovalBoard board = new BpmApprovalBoard();
        board.setStartedNum(bpmProcActorMapper.countNum(userId, BpmProcActorType.APPLICAT, null));
        board.setWaitNum(bpmProcActorMapper.countNum(userId, BpmProcActorType.APPROVER, BpmProcActorHandleStatus.WAIT));
        board.setHandledNum(bpmProcActorMapper.countNum(userId, BpmProcActorType.APPROVER, BpmProcActorHandleStatus.HANDLED));
        board.setNotifyNum(bpmProcActorMapper.countNum(userId, BpmProcActorType.NOTIFY, null));
        board.setUnreadNum(bpmProcActorMapper.countNum(userId, BpmProcActorType.NOTIFY, BpmProcActorHandleStatus.WAIT));
        return board;
    }
}
